// --- Copyright (c) 2006-2008 Stefan Kyntchev ---
// This software is written and copyrighted by Stefan Kyntchev 
// and BeyondPod Team members. All rights are reserved.
// Author contact: support@beyondpod.mobi
// ------------------------------------------------------------------------
// This file is part of BeyondPod RSS Feed Reader and Podcast manager
// (www.codeplex.com/BeyondPod) 
// BeyondPod is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// BeyondPod is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more details. 
//  
// You should have received a copy of the GNU General Public License
// along with BeyondPod. If not, see <http://www.gnu.org/licenses/>
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 


using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using svs.Mobile.BeyondMedia.FeedCore;
using svs.Mobile.BeyondMedia.FeedCore.Helpers;
using svs.Mobile.BeyondMedia.FeedCore.RSS;
using svs.Mobile.BeyondMedia.Forms;
using svs.Mobile.BeyondMedia.Objects;
using svs.Mobile.BeyondMedia.Properties;
using svs.Mobile.BeyondMedia.UiCore.KineticControls;


namespace svs.Mobile.BeyondMedia.Controls
{
	/// <summary>
	/// Provide a 2 line listview
	/// </summary>
	internal class KineticFeedsView : KineticListView, IFeedsView
	{
		protected Feed m_Feed;

		private readonly Timer m_EnterTimer = new Timer();
		protected bool m_IsLocalKeyUp;
		public event EventHandler ReturnLongPress;

		public event Helpers.FeedSelectedEvent Select;
		public event Helpers.NavigationEvent Navigation;
		public event EventHandler SelectedIndexChanged;

		readonly FeedList m_Items = new FeedList();

		public bool AutoShowHeader { get; set; }

		#region Nested type: ReloadDelegate

		private delegate void ReloadDelegate();

		#endregion

		#region Nested type: TrackCountChangedDelegate

		private delegate void TrackCountChangedDelegate(Track i_track);

		#endregion

		#region Event Handlers

		private void OnNavigation(Helpers.NavigationDirection i_direction)
		{
			if (Navigation != null)
				Navigation(this, i_direction);
		}

		protected override void OnSelectionChanged(int target)
		{
			if (SelectedIndexChanged != null)
				SelectedIndexChanged(this, EventArgs.Empty);
		}

		#endregion

		#region Constructors

		public KineticFeedsView(bool supportCategories)
		{
			FeedRepository.TrackDeleted += OnTrackCountChanged;
			FeedRepository.TrackAdded += OnTrackCountChanged;

			if (supportCategories)
			{
				m_EnterTimer.Tick += OnLongPressReturn;
				m_EnterTimer.Interval = 700;
				FeedImageList.Initialize(Settings.PrimaryIconPixSize, Settings.ScreenDPI);
			}
		}

		#endregion

		#region Public Properties

		public FeedList Items
		{
			get
			{
				return m_Items;
			}
		}

		/// <summary>
		/// Gets the selected feed.
		/// </summary>
		/// <value>The selected feed.</value>
		public Feed SelectedFeed
		{
			get
			{
				if (SelectedIndex >= 0 && SelectedIndex < Items.Count)
					return Items[SelectedIndex];

				return null;
			}

			set
			{
				SelectedIndex = GetItemIndex(value);
			}
		}

		private int GetItemIndex(Feed value)
		{
			if (Items.Count > 0 && value != null)
					return Items.IndexOf(value);

			return -1;
		}

		/// <summary>
		/// The current Feed
		/// </summary>
		public Feed Feed
		{
			get
			{
				return m_Feed;
			}
		}

		protected override string IfEmptyText
		{
			get
			{
				return Configuration.ActiveFeedCategory == CategoryManager.AllFeeds ? Resources.FeedViewIsEmpty : Resources.FeedViewCategoryIsEmpty;
			}
		}

		/// <summary>
		/// The name of the window
		/// </summary>
		public string Title
		{
			get
			{
				return string.Format("{0} {1}",
				                     Configuration.ActiveFeedCategory == CategoryManager.AllFeeds ? "All Feeds" : "Feeds",
				                     m_Items == null || m_Items.Count == 0 ? string.Empty : "(" + m_Items.Count + ")");
			}
		}

		/// <summary>
		/// The menu
		/// </summary>
		public MainMenu ViewMenu
		{
			get
			{
				return MenuManager.GetMenu(Helpers.ViewNames.Feeds);
			}
		}

		#endregion

		#region Public Methods

		public void OnAfterDeactivate()
		{
		}

		public void OnAfterActivate()
		{
		}

		public void OnBeforeActivate()
		{
			Focus();
		}

		public void Clear()
		{
			m_Feed = null;
			Items.Clear();
			base.ClearItems();
		}

		public void Reload()
		{
			if (Master.MasterForm.InvokeRequired)
			{
				Master.MasterForm.Invoke(new ReloadDelegate(Reload));
				return;
			}

			if (Feed == null)
				return;

			GenerateListItems(Feed);
			SetRootFeedInternal(SelectedIndex, true);
		}

		public void SetRootFeed(Feed root, Feed selected)
		{
			// if selected is null, but an old SelectedFeed is set, then we try to find 
			// a feed with the same name in the new list. This will reselect the feed after 
			// reloading the repositiory.
			Guid OldSelId = Guid.Empty;
			if (selected == null && SelectedFeed != null)
				OldSelId = SelectedFeed.ID;

			GenerateListItems(root);

			if (OldSelId != Guid.Empty)

			  Items.ForEach(delegate(Feed f)
		    {
		  		if (f.ID==OldSelId)
						selected=f;
			  });

			SetRootFeedInternal(selected == null ? 0 : Items.IndexOf(selected), false);
		}

		private void SetRootFeedInternal(int selectedIndex, bool preserveScroll)
		{
			int index = -1;

			if (Items.Count > 0)
			{
				if (selectedIndex == -1 || selectedIndex >= 0 && selectedIndex < Items.Count)
					index = selectedIndex;
				else if (selectedIndex >= Items.Count)
					index = Items.Count - 1;
			}

			if (AutoShowHeader)
			{
				SetItemsAndShowHeader(BuildNativeControlItems());
				AutoShowHeader = false;
			}
			else
				SetItems(BuildNativeControlItems(), index, preserveScroll);
		}

		private void GenerateListItems(Feed root)
		{
			m_Feed = root;
			Items.Clear();

			if (root == null)
				return;

			FilterByCategory();

			if (Feed.CanNavigateUp)
				Items.Insert(0, FeedRepository.GoUpPlaceholder);
		}

		public Feed GetNextUnreadFeed()
		{
			if (SelectedIndex >= 0 && Items.Count > 0)
			{
				int i = SelectedIndex == Items.Count - 1 ? 0 : SelectedIndex + 1;

				while (i != SelectedIndex)
				{
					if (Items[i].HasUnreadItems)
						return Items[i];

					if (++i == Items.Count)
						i = 0;
				}
			}
			return null;
		}

		public Feed GetNextFeed()
		{
			if (SelectedIndex < Items.Count - 1)
				return Items[SelectedIndex + 1];

			return null;
		}

		public Feed GetPreviousFeed()
		{
			if (SelectedIndex > 0)
				return Items[SelectedIndex - 1];

			return null;
		}

		public void NotifyFeedStatusChanged(Feed feed)
		{
			int inx = GetItemIndex(feed);

			if (inx != -1)
			{
				OnSelectionChanged(inx); //this will cause the menus to refressh correctly
				UpdateItem(BuiltNativeControlItem(feed), inx);
			}
		}

		#endregion

		#region Protected Methods

		protected override void OnCustomizeControlSettings(ListViewSettings settings)
		{
			settings.ListNoteTextColor = Color.Green;
			settings.ListNoteSelectedTextColor = Color.Green;
		}

		protected virtual void FilterByCategory()
		{
			Items.AddRange(Feed.Feeds.FindAll(CategoryManager.IsInActiveCategory));
			if (Configuration.ActiveFeedCategory == CategoryManager.AllFeeds)
			{
				GroupByFirstCategory(Items);
			}
		}

		private static void GroupByFirstCategory(FeedList items)
		{
			var groups = new Dictionary<FeedCategory, FeedList>();

			items.ForEach(delegate(Feed f)
			{
				if (!groups.ContainsKey(f.Categories.Primary))
					groups[f.Categories.Primary] = new FeedList();

				groups[f.Categories.Primary].Add(f);
			});

			items.Clear();

			var categories = new List<FeedCategory>(groups.Keys);
			categories.Sort();

			categories.ForEach(delegate(FeedCategory c)
			{
				groups[c].Sort(new FeedComparer<Feed>(Configuration.FeedsSortOrder));
				items.AddRange(groups[c]);
			});
		}

		protected void OnLongPressReturn(object sender, EventArgs e)
		{
			m_EnterTimer.Enabled = false;
			m_IsLocalKeyUp = false;

			if (ReturnLongPress != null)
				ReturnLongPress(this, null);
		}

		protected override void OnKeyUp(KeyEventArgs e)
		{
			m_EnterTimer.Enabled = false;

			try
			{
				switch (e.KeyCode)
				{
					case Keys.Return:

						if (Select != null && SelectedIndex != -1 && m_IsLocalKeyUp)
						{
							Select(SelectedFeed, FeedViewType.RSS);
						}

						e.Handled = true;
						break;
				}

				if (!e.Handled)
				{
					base.OnKeyUp(e);
				}
			}
			finally
			{
				m_IsLocalKeyUp = false;
			}
		}

		protected override void OnKeyDown(KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
				case Keys.Return:
					m_IsLocalKeyUp = true;
					m_EnterTimer.Enabled = true;
					e.Handled = true;
					break;

				//Left Right
				case Keys.Left:
					OnNavigation(Helpers.NavigationDirection.Back);
					e.Handled = true;
					break;

				//Left Right
				case Keys.Right:

					if (SelectedFeed != null && SelectedFeed.Tracks.Count > 0)
						CommandManager.CmdPlaySelectedFeed();
					else
            OnNavigation(Helpers.NavigationDirection.Forward);

					e.Handled = true;
					break;
			}

			if (!e.Handled)
			{
				base.OnKeyDown(e);

				if (!e.Handled)
					e.Handled = ShortcutKeyManager.HandleKey(e.KeyCode);
			}
		}

		protected override void  OnSecondaryIconClicked(int index)
		{
			if (Items[index].AllowEnclosures && Items[index].Tracks.Count > 0)
			{
				CommandManager.CmdPlaySelectedFeed();
			}
			else
			{
				OnItemBodyClicked(index);
			}
		}

		protected override void  OnItemBodyClicked(int index)
		{
			if (Select != null)
			{
				Application.DoEvents();
				Select(SelectedFeed, FeedViewType.RSS);
			}
		}

		protected override List<ListViewItemData> BuildNativeControlItems()
		{
			var retVal = new List<ListViewItemData>(m_Items.Count);

			foreach (var feed in m_Items)
			{
				retVal.Add(BuiltNativeControlItem(feed));
			}

			return retVal;
		}

		protected virtual ListViewItemData BuiltNativeControlItem(Feed feed)
		{
			var group = feed.IsSubFeed
			            	? Feed.Name
			            	: Configuration.ActiveFeedCategory == CategoryManager.AllFeeds
			            	  	? feed.Categories.Primary.Name
			            	  	: Configuration.ActiveFeedCategory.Name;

			var i = new ListViewItemData
			{
				Group = group,
				PrimaryText = feed.Name,
				SecondaryText = GetInfoTextForFeed(feed),
				PrimaryImage = null,
				OverlayImage = null,
				SecondaryImage = null,
			};

			if (feed.Equals(FeedRepository.GoUpPlaceholder))
			{
				i.PrimaryImage = FeedImageList.GoUpImage;
				i.PrimaryText = string.Format("Back to {0}",
				                              Feed.ParentFeed.IsSubFeed
				                              	? Feed.ParentFeed.Name
				                              	: Configuration.ActiveFeedCategory.Name);
			}
			else if (feed.Type == FeedType.LocalFolder)
			{
				i.PrimaryImage = feed.IsSubFeed ? FeedImageList.DefaultSubAudioFolderImage : FeedImageList.DefaultAudioFolderImage;
			}
			else if (feed.Type == FeedType.OPMLFeed)
			{
				i.PrimaryImage = FeedImageList.DefaultOpmlImage;
			}
			else if (feed.Type == FeedType.RSSFeed || feed.Type == FeedType.RSSFeedWithPodcasts)
			{
				i.PrimaryImage = FeedImageList.GetFeedImageFor(feed);
			}

			if (!feed.CanDeleteTracks)
			{
				i.OverlayImage = FeedImageList.LockedOverlay;
			}

			if (feed.UpdateFailed || feed.DownloadFailed)
			{
				i.OverlayImage = FeedImageList.WarnOverlay;
			}

			if (feed.AllowEnclosures && feed.Tracks.Count > 0)
			{
				i.SecondaryImage = FeedImageList.FeedHasPodcastImage;
				i.Note = feed.Tracks.Count.ToString();
			}
			else if(feed.Feeds.Count > 0)
			{
				i.SecondaryImage = FeedImageList.MoreArrowImage;
			}

			if (feed.HasUnreadItems)
				i.BoldPrimary = true;

			return i;
		}

		private static string GetInfoTextForFeed(Feed feed)
		{
			string InfoText = null;
			//For the feed that is currently updating
			if (RssFeedUpdateManager.IsUpdating && RssFeedUpdateManager.CurrentFeed == feed)
			{
				if (feed.UpdateSize <= 0)
					InfoText = string.Format("Updating {0}...", feed.UpdatedPortion > 0 ? CoreHelper.GetFileLengthAsString(feed.UpdatedPortion) : string.Empty);
				else
					InfoText = string.Format("Updating {0}% of {1}...", (int)feed.UpdatePercent, CoreHelper.GetFileLengthAsString(feed.UpdateSize));
			}
			// For the feed that is currently downloading
			else if (EnclosureDownloadManager.IsDownloading && EnclosureDownloadManager.CurrentFeed == feed)
			//the current downloading feed
			{
				InfoText =
					string.Format("Downloading {0} of {1}...", feed.NumDownloadedEnclosures + 1,
												feed.NumEnclosuresAvailableForDownload);
			}
			// feeds that are waiting to be downloaded
			else if (EnclosureDownloadManager.IsDownloading && !feed.DownloadFailed && feed.NumEnclosuresAvailableForDownload > 0 &&
							 feed.NumDownloadedEnclosures == 0)
			{
				InfoText = string.Format("{0} Pending...", feed.NumEnclosuresAvailableForDownload);
			}
			// general case where a feed can have podcasts and does not have child feeds
			else if (feed.DownloadPathExists && feed.Feeds.Count == 0)
			{
				string newencl = string.Empty;

				if (feed.NumEnclosuresAvailableForDownload > 0)
				{
					if (feed.DownloadFailed)
					{
						if (feed.NumDownloadedEnclosures == 0)
							newencl = (feed.NumEnclosuresAvailableForDownload - feed.NumDownloadedEnclosures) + " Failed!   ";
						else if (feed.Tracks.Count > 0)
							newencl = string.Format("{0} of {1} New   ", feed.NumDownloadedEnclosures,
																			feed.NumEnclosuresAvailableForDownload);
					}
					else if (feed.NumDownloadedEnclosures > 0 && feed.Tracks.Count > 0)
						newencl = string.Format("{0} New   ", feed.NumDownloadedEnclosures);
				}

				InfoText =
					string.Format("{0}{1}", CoreHelper.FormatDate(feed.LastModifiedDate), newencl.Length > 0 ? ", " + newencl : string.Empty);
			}
			// when the feed has child feeds
			else if (feed.DownloadPathExists)
			{
				string date = CoreHelper.FormatDate(feed.LastModifiedDate);
				InfoText = string.Format("{0}{1}{2} Feeds",date, string.IsNullOrEmpty(date)? string.Empty: ",  ", feed.Feeds.Count);
			}
			// for RSS feeds without podcasts
			else
			{
				InfoText = CoreHelper.FormatDate(feed.LastModifiedDate);
			}

			return InfoText.Length > 29 ? InfoText.Substring(0, 29) : InfoText;
		}

		private void OnTrackCountChanged(Track i_track)
		{
			if (InvokeRequired)
			{
				TrackCountChangedDelegate cbDel = OnTrackCountChanged;

				Invoke(cbDel, i_track);
				return;
			}

			NotifyFeedStatusChanged(i_track.ParentFeed);
		}

		#endregion


	}
}